#pragma once

#include <iostream>
#include <string>
#include <pthread.h>
#include <fstream>
#include <vector>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unordered_map> //关联容器,包含可以重复的键值对

#include "Socket.hpp"
#include "Log.hpp"

const std::string wwwroot = "./wwwroot";   // web根目录
const std::string sep = "\r\n";            // http报文分隔符
const std::string homepage = "index.html"; // 索引页面

static const int defaultport = 8082;

class HttpServer;

class ThreadData
{
public:
    ThreadData(int fd, HttpServer *s) : sockfd(fd), svr(s) {}

public:
    int sockfd;
    HttpServer *svr;
};

class HttpRequest
{
public:
    void Deserialize(std::string req) // 分离报文
    {
        while (true)
        {
            std::size_t pos = req.find(sep);
            if (pos == std::string::npos)
            {
                break;
            }
            std::string temp = req.substr(0, pos);
            if (temp.empty())
                break;
            req_header.push_back(temp); // 将http请求报文按行分隔
            req.erase(0, pos + sep.size());
        }
        text = req; // 将请求正文存到text
    }

    // .png:image/png
    void Parse() // 获取文件路径与拓展名
    {
        std::stringstream ss(req_header[0]); // std::stringstream是一个流类，它允许像控制台输入/输出那样处理内存中的字符串
        ss >> method >> url >> http_version; // 解析包含多个字段的字符串
        file_path = wwwroot;                 //./wwwroot
        if (url == "/" || url == "/index.html")
        {
            file_path += "/";
            file_path += homepage; // ./wwwroot/index.html
        }
        else
            file_path += url; /// a/b/c/d.html->./wwwroot/a/b/c/d.html

        auto pos = file_path.rfind(".");
        if (pos == std::string::npos)
            suffix = ".html";
        else
            suffix = file_path.substr(pos);
    }

    void DebugPrint() // 打印请求报文
    {
        for (auto &line : req_header)
        {
            std::cout << "----------------------------" << std::endl;
            std::cout << line << "\n\n";
        }
        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "http_version: " << http_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;
        std::cout << text << std::endl;
    }

public:
    std::vector<std::string> req_header; // 请求报头+请求行
    std::string text;                    // 请求正文

    // 请求行解析之后的结果
    std::string method;       // 请求方法
    std::string url;          // 统一资源定位符
    std::string http_version; // http版本
    std::string file_path;    // 文件路径

    std::string suffix; // 文件拓展名
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport) : port_(port)
    {                                                // 如果不包含该字段，浏览器可能无法正确地解析和显示响应体内容
        content_type.insert({".html", "text/html"}); // 拓展名->正文类型
        content_type.insert({".jpg", "image/jpg"});
        content_type.insert({".png", "image/png"});
    }

    bool Start()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        for (;;)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = listensock_.Accept(&clientip, &clientport);
            if (sockfd < 0)
            {
                continue;
            }
            lg(Info, "get a new connect,sockfd:%d", sockfd);
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, this);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }

    static std::string ReadHtmlContent(const std::string &htmlpath)
    {
        std::ifstream in(htmlpath, std::ios::binary); // 以二进制方式读取文件
        if (!in.is_open())
            return "";

        in.seekg(0, std::ios_base::end); // 将文件读取指针移动到文件的末尾
        auto len = in.tellg();           // tellg函数返回当前文件读取指针的位置
        in.seekg(0, std::ios_base::beg); // 将文件读取指针重新移动回文件的开头

        std::string content;
        content.resize(len);

        in.read((char *)content.c_str(), content.size()); // 读取html文件到content
        in.close();
        return content;
    }

    std::string SuffixToDesc(const std::string &suffix) // 获取正文类型
    {
        auto iter = content_type.find(suffix);
        if (iter == content_type.end())
            return content_type[".html"];
        else
            return content_type[suffix];
    }

    void HandlerHttp(int sockfd) // http报文处理
    {
        char buffer[20240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // ssize_t 是一个有符号整数类型,它通常用于表示可能为正或负的对象大小；size_t 是一个无符号整数类型，它是 sizeof 运算符返回的类型
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl;
            HttpRequest req;
            req.Deserialize(buffer); // 报文的反序列化
            req.Parse();             // 获取文件路径与拓展名
            // req.DebugPrint();

            // 返回响应
            std::string text;
            bool ok = true;
            text = ReadHtmlContent(req.file_path); // 通过反序列化后得到的文件路径读取文件到text
            if (text.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHtmlContent(err_html);
            }

            std::string response_line; // 状态行
            if (ok)
                response_line = "HTTP/1.0 200 OK\r\n";
            else
                response_line = "HTTP/1.0 404 Not Found\r\n";

            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size()); // 正文长度
            response_header += "\r\n";
            response_header += "Content-type: "; // 正文类型
            response_header += SuffixToDesc(req.suffix);
            response_header += "\r\n";
            response_header += "Set-Cookie: name=haha&&passwd=12345"; // cookie文件提供了http对登录用户的会话保持功能
            response_header += "\r\n";
            // response_header += "Location: https://www.qq.com\r\n";
            std::string blank_line = "\r\n"; // \n

            // 整合回应报文
            std::string response = response_line;
            response += response_header;
            response += blank_line;
            response += text;

            send(sockfd, response.c_str(), response.size(), 0);
        }
    }

    static void *ThreadRun(void *args) // 线程运行函数
    {
        pthread_detach(pthread_self()); // 分离线程
        ThreadData *td = static_cast<ThreadData *>(args);
        td->svr->HandlerHttp(td->sockfd); // 静态成员函数不可以直接调用成员函数，通过将this指针封装在threadData中，借助封装好的this指针去调用成员函数
        delete td;
        return nullptr;
    }

private:
    Sock listensock_;
    uint16_t port_;
    std::unordered_map<std::string, std::string> content_type; // compair<文件拓展名，文件类型>
};